package com.huan.social.weixin.connect;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.springframework.social.oauth2.AccessGrant;
import org.springframework.social.oauth2.OAuth2Template;
import org.springframework.util.MultiValueMap;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Map;

/**
 * 走完 oauth2 的授权码协议，获取accessToken和openId
 *
 * @author huan.fu
 * @date 2018/9/27 - 11:38
 */
@Slf4j
@Getter
@Setter
public class WeixinOauth2Template extends OAuth2Template {

	private String clientId;
	private String clientSecret;
	private String authorizeUrl;
	private String accessTokenUrl;
	private String refreshTokenUrl;

	public WeixinOauth2Template(String clientId, String clientSecret, String authorizeUrl, String accessTokenUrl, String refreshTokenUrl) {
		super(clientId, clientSecret, authorizeUrl, accessTokenUrl);
		// 取消父类会自动加到 restTemplate 上的 basic 认证
		super.setUseParametersForClientAuthentication(true);
		this.clientId = clientId;
		this.clientSecret = clientSecret;
		this.authorizeUrl = authorizeUrl;
		this.accessTokenUrl = accessTokenUrl;
		this.refreshTokenUrl = refreshTokenUrl;
	}

	/**
	 * 获取访问令牌
	 *
	 * @param authorizationCode
	 * @param redirectUri
	 * @param additionalParameters
	 * @return
	 */
	@Override
	public AccessGrant exchangeForAccess(String authorizationCode, String redirectUri, MultiValueMap<String, String> additionalParameters) {
		StringBuilder builder = new StringBuilder(accessTokenUrl);
		builder.append("?appid=").append(clientId)
				.append("&secret=").append(clientSecret)
				.append("&code=").append(authorizationCode)
				.append("&grant_type=authorization_code")
				.append("&redirect_uri=").append(formEncode(redirectUri));
		if (null != additionalParameters && !additionalParameters.isEmpty()) {
			additionalParameters.forEach((key, value) -> builder.append("&").append(formEncode(key)).append("=").append(value));
		}
		return getAccessToken(builder);
	}

	/**
	 * 刷新访问令牌
	 *
	 * @param refreshToken
	 * @param additionalParameters
	 * @return
	 */
	@Override
	public AccessGrant refreshAccess(String refreshToken, MultiValueMap<String, String> additionalParameters) {
		StringBuilder builder = new StringBuilder(this.getRefreshTokenUrl());
		builder.append("?appid=").append(clientId)
				.append("&grant_type=refresh_token")
				.append("&refresh_token=").append(refreshToken);
		if (null != additionalParameters && !additionalParameters.isEmpty()) {
			additionalParameters.forEach((key, value) -> builder.append("&").append(formEncode(key)).append("=").append(value));
		}
		return getAccessToken(builder);
	}

	/**
	 * 获取访问令牌，注意这个地方微信会多返回一些信息，比如：openId等等
	 *
	 * @param accessUrl
	 * @return
	 */
	private AccessGrant getAccessToken(StringBuilder accessUrl) {
		String response = getRestTemplate().getForObject(accessUrl.toString(), String.class);
		Map<String, Object> result = new Gson().fromJson(response, new TypeToken<Map<String, Object>>() {
		}.getType());
		return new WeixinAccessGrant(
				MapUtils.getString(result, "access_token"),
				MapUtils.getString(result, "scope"),
				MapUtils.getString(result, "refresh_token"),
				MapUtils.getLong(result, "expires_in"),
				MapUtils.getString(result, "openid"));
	}

	/**
	 * 编码数据
	 *
	 * @param data
	 * @return
	 */
	private String formEncode(String data) {
		try {
			return URLEncoder.encode(data, "UTF-8");
		} catch (UnsupportedEncodingException ex) {
			// should not happen, UTF-8 is always supported
			throw new IllegalStateException(ex);
		}
	}
}
